﻿// Inner Fire 游戏引擎库
// Avatar - 万用骨骼（Genetic Skeleton）的躯体(Avatar)
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于万用骨骼的印射。将不同种类的动画中的命名统一成一个通用骨骼（Genetic
// Skeleton）。为了统一，命名和Unity或是UE中的命名一致。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-29

#include "avatar.h"
#include "enum_parser.h"
#include "klog.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h> // 包含Assimp的后处理步骤
#include <assimp/scene.h>       // 包含Assimp的场景结构体
#include <functional>
#include <game/config/config_manager.h>
#include <game/resource/assets.h>
#include <graphics/imath.h>
#include <kfile.h>

using namespace DirectX;

namespace ifire::game {

AvatarMapping::AvatarMapping(ikit::sstr _bone, int _id)
    : bone(_bone), id(_id) {}

AvatarMapping::AvatarMapping(
    const AvatarGroup* group, const ikit::XmlNode& node)
    : bone(node.Value()), id(group->GetID(node("Name"))) {}

Avatar::Avatar(ConfigManager* manager, const ikit::XmlNode& node)
    : ConfigInfo(manager, node("Name"))
    , pose_file_(node("PoseFile"))
    , convert_units_(node("ConvertUnits")) {
  // 获得骨骼（要使这个生效，AvatarGroup必须在之前载入）
  group_ = manager->GetAvatarGroup(node("Group"));

  // 载入印射项目
  node.Foreach("Map",
      [this](ikit::XmlNode& n, int) { mapping_.emplace_back(group_, n); });
  // 载入骨骼项目
  node.ReadItems("Bone", pose_bones_);
  // 载入武器附着项目
  node.ReadPtrs("Attach", attachs_);
  // 载入动态骨骼项目
  node.ReadItems("PhysicsBone", physics_bones_);

  // 建立快速访问的项目
  for (size_t i = 0; i < pose_bones_.size(); i++) {
    auto& item = pose_bones_[i];
    pose_bones_mapping_[item.bone.uid()] = i;
  }

  // 从文件中载入对应的信息，如果是载入的信息，则打印
  if (!pose_file_.empty()) {
    Load();
    auto new_file = replace_suffix_extension(pose_file_, "_Armature", ".xml");
    KLOG("Write Armature: %s", new_file.c_str());
    tree_->PrintArmatureXml(ikit::to_wide(new_file));
  } else { // 如果没有指定文件，则表示Avatar中有配置，从配置中载入
    tree_ = std::make_unique<ArmatureTree>(node.Get("Armature"));
  }

  // Avatar的骨架，都会配置好ID
  tree_->SetByAvatar(this);
}

int Avatar::FindId(ikit::sstr name) const {
  for (auto& e : mapping_) {
    if (e.bone == name) {
      return e.id;
    }
  }
  return AVATAR_NONE_ID;
}

ikit::sstr Avatar::FindBone(int id) const {
  for (auto& e : mapping_) {
    if (e.id == id) {
      return e.bone;
    }
  }

  return ikit::sstr();
}

const AvatarAttach* Avatar::GetAttachSlot(ikit::sstr slot_name) const {
  // 遍历Plan，得到合适的项目
  for (auto& e : attachs_) {
    if (e->name == slot_name) {
      return e.get();
    }
  }
  return nullptr;
}

AvatarPoseBone& Avatar::GetPoseBone(ikit::sstr name) {
  // 调用const版本，然后去除const限定
  return const_cast<AvatarPoseBone&>(
      static_cast<const Avatar*>(this)->FindPoseBone(name));
}

const AvatarPoseBone& Avatar::FindPoseBone(ikit::sstr name) const {
  static AvatarPoseBone EMPTY_POSE_BONE = AvatarPoseBone();

  if (name.empty()) {
    return EMPTY_POSE_BONE;
  }

  auto it = pose_bones_mapping_.find(name.uid());
  if (it == pose_bones_mapping_.end()) {
    return EMPTY_POSE_BONE;
  }

  return pose_bones_[it->second];
}

const AvatarPoseBone& Avatar::FindPoseBone(int id) const {
  return FindPoseBone(FindBone(id));
}

void Avatar::CalcDiff(Avatar* b, MatrixList& list) {
  CalcDiffHierarchy(ID_MAT, tree_->Root(), b, list);
}

void Avatar::CalcDiffHierarchy(
    FXMMATRIX parent_mat, ArmatureNode* node, Avatar* b, MatrixList& list) {
  // PS：这里先拿A-Pose做实验

  // 计算出当前的项目
  XMMATRIX trans = node->transformation;
  XMMATRIX current = XMMatrixMultiply(trans, parent_mat);

  // 如果没有Avatar的印射，这个项目应该继承Parent
  if (node->avatar_id != AVATAR_NONE_ID) {
    // 得到目标的Node
    auto b_name = b->FindBone(node->avatar_id);
    auto b_node = b->tree_->Find(b_name);
    // 如果目标不存在，则继续
    if (b_node) {
    }
  }

  // 继续
  for (size_t i = 0; i < node->children.size(); i++) {
    CalcDiffHierarchy(current, node->children[i], b, list);
  }
}

void Avatar::Print() const {
  KLOG("\n== Avatar(%s) ==\n", name.c_str());
  for (auto& e : mapping_) {
    KLOG("%s => %s", e.bone.log_str(),
        e.id == AVATAR_NONE_ID ? "NONE" : group_->Get(e.id).name.c_str());
  }
}

void Avatar::PrintXml(const ArmatureTree* target, const char* file_name) const {
  ikit::file_printer f(file_name);
  XMFLOAT3 p, r;

  for (auto i : target->Nodes()) {
    Trans3 t(i->transformation);
    p = t.GetPosition();
    r = t.GetRotationEuler();

    f.print(
        "<Bone Name='%s' Pos='%.5f,%.5f,%.5f' Rot='%.5f,%.5f,%.5f'></Bone>\n",
        i->name.c_str(), p.x, p.y, p.z, r.x, r.y, r.z);

    auto& pose = FindPoseBone(i->name);
    if (pose.bone.empty()) {
      f.println("*NOT FOUND*");
    } else {
      XMStoreFloat3(&p, pose.transform.position);
      r = imath::QuaternionToEulerDegree(pose.transform.rotation);

      f.print("<Pose Name='%s' Pos='%.5f,%.5f,%.5f' "
              "Rot='%.5f,%.5f,%.5f'></Pose>\n\n",
          pose.bone.c_str(), p.x, p.y, p.z, r.x, r.y, r.z);
    }
  }
}

void Avatar::Setup() {
  for (auto& e : physics_bones_) {
    e.setting = manager_->GetPhysicsBoneSetting(e.setting_name);
  }
}

void Avatar::Load() {
  Assimp::Importer importer;
  // 移除网格、材质、光照等数据，仅保留动画
  importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS,
      aiComponent_MATERIALS | aiComponent_LIGHTS | aiComponent_CAMERAS);
  // 设置标志
  int flag = aiProcess_ValidateDataStructure | aiProcess_ConvertToLeftHanded;

  // 载入
  auto scene = importer.ReadFile(pose_file_.c_str(), flag);
  if (!scene) {
    const char* err = importer.GetErrorString();
    KERROR("ERROR::ASSIMP:: %s",
        err ? err : "Unknown error (possible file not found)");
    return;
  }

  // 创建骨架
  tree_ = std::make_unique<ArmatureTree>(scene->mRootNode, convert_units_);
}

AvatarPoseBone::AvatarPoseBone() : bone(), transform(), final_mat(ID_MAT) {}

AvatarPoseBone::AvatarPoseBone(const ikit::XmlNode& node)
    : bone(node("Name")), transform(), final_mat(ID_MAT) {
  DirectX::XMFLOAT3 pos = node("Pos");
  DirectX::XMFLOAT3 rot = node("Rot");
  // 对坐标轴进行取反
  pos.x = -pos.x;
  pos.z = -pos.z;
  rot.x = -rot.x;
  rot.z = -rot.z;

  transform = {pos, imath::EulerToQuaternion(rot)};

  // 进行坐标轴的反转
  // transform.position.z = -transform.position.z;
}

AvatarPhysicsBone::AvatarPhysicsBone()
    : bone(), setting_name(), setting(nullptr) {}

AvatarPhysicsBone::AvatarPhysicsBone(const ikit::XmlNode& node)
    : bone(node("Bone")), setting_name(node("Setting")), setting(nullptr) {}

} // namespace ifire::game